#include <process.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>

#include "lib.h"
#include "thread.h"
using namespace Library;


//----------------------------------------------------------------------------
// Public
//
Thread::Thread(HINSTANCE hInstance, HWND hWnd, const String & name)
{
	m_name		= name;
	m_state		= TS_SUSPENDED;
	m_thread	= 0;
	m_threadId	= 0;
	m_self		= 0;
	m_parent	= hWnd;
	m_hinstance	= hInstance;
	m_class		= 0;

	InitializeCriticalSection(&m_mutex);

	m_thread = (HANDLE)_beginthreadex( NULL, 0, &s_threadProc, (void *)this, CREATE_SUSPENDED, &m_threadId );

	// Handle thread creation errors.
	if (m_thread == 0)
		m_state = TS_STOPPED;
}

Thread::~Thread()
{
	DeleteCriticalSection(&m_mutex);
}

bool8
Thread::resume()
{
	bool8	success = false;

	beginCS();

	// If the thread is currently suspended, resume operation.
	if (m_state == TS_SUSPENDED && ResumeThread(m_thread) != -1)
		m_state = TS_RUNNING;

	// If the thread was already running or was successfully resumed
	// above, then success!
	if (m_state == TS_RUNNING)
		success = true;

	endCS();

	return success;
}

bool8
Thread::suspend()
{
	bool8	success = false;

	beginCS();

	// If the thread is currently running, suspend operation.
	if (m_state == TS_RUNNING && SuspendThread(m_thread) != -1)
		m_state = TS_SUSPENDED;

	// If the thread was already suspended or was successfully suspended
	// above, then success!
	if (m_state == TS_SUSPENDED)
		success = true;

	endCS();

	return success;
}

/**
 * Standard method of stopping a thread.  This method allows the thread time
 * clean up before exiting.
 */
bool8
Thread::stop()
{
	bool8	success = false;

	beginCS();

	// If the thread is currently suspended, resume operation.
	if (m_state == TS_SUSPENDED && ResumeThread(m_thread) != -1)
		m_state = TS_RUNNING;

	// If the thread was was successfully resumed above, or already running,
	// and PostThreadMessage() succeeded then success!
	if (m_state == TS_RUNNING && PostThreadMessage(m_threadId, TM_STOP, 0, 0))
		success = true;

	endCS();

	return success;
}

/**
 * Brute force method to terminate a thread.  This should be called only as
 * a last resort, since the thread is allow zero time to clean up.
 */
bool8
Thread::kill()
{
	bool8	success = false;

	beginCS();

	// If the thread is currently running, terminate operation.
	if (m_state != TS_STOPPED && TerminateThread(m_thread, 0) != 0)
		m_state = TS_STOPPED;

	// If the thread was already stopped or was successfully terminated
	// above, then success!
	if (m_state == TS_STOPPED)
		success = true;

	endCS();

	return success;
}


//----------------------------------------------------------------------------
// Protected
//
bool8
Thread::createWindow()
{
	String		className;
	String		windowName;
	WNDCLASSEX	wcx;

	className.sprintf("T%04X_%sClass", m_threadId, m_name);
	windowName.sprintf("T%04X_%sWindow", m_threadId, m_name);

	wcx.cbSize			= sizeof(wcx);
	wcx.style			= 0;
	wcx.lpfnWndProc		= s_windowProc;
	wcx.cbClsExtra		= 0;
	wcx.cbWndExtra		= 0;
	wcx.hInstance		= m_hinstance;
	wcx.hIcon			= 0;
	wcx.hCursor			= 0;
	wcx.hbrBackground	= 0;
	wcx.lpszMenuName	= 0;
	wcx.lpszClassName	= className;
	wcx.hIconSm			= 0;

	m_class = RegisterClassEx(&wcx);

	if (!m_class)
		return false;

//	m_self = CreateWindow(className, windowName, WS_OVERLAPPEDWINDOW,
//				CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
//				m_parent, 0, m_hinstance, this);
	m_self = CreateWindow(className, windowName, WS_DISABLED | WS_POPUP,
				0, 0, 1, 1, 0, 0, m_hinstance, this);

	if (!m_self)
	{
		destroyWindow();
		return false;
	}

	SetWindowLong(m_self, GWL_USERDATA, (LONG)this);
	ShowWindow(m_self, false);

	return true;
}

void
Thread::destroyWindow()
{
	if (m_self)
	{
		DestroyWindow(m_self);
		m_self = 0;
	}

	if (m_class)
	{
		UnregisterClass((LPCTSTR)m_class, m_hinstance);
		m_class = 0;
	}
}

int32
Thread::windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
		case TM_START:
			return 0;

		case TM_STOP:
//			PostThreadMessage(m_threadId, WM_QUIT, 0, 0);
			PostQuitMessage(0);
			return 0;

		default:
			break;
	}

	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

//----------------------------------------------------------------------------
// Private
//

/**
 * Thread message pump.
 */
UINT WINAPI
Thread::s_threadProc(void* arg)
{
	Thread* This = (Thread*)arg;

	MSG		msg;
	memset( &msg, 0, sizeof(msg) );

	if (This->createWindow())
	{
		PostThreadMessage(This->m_threadId, TM_START, 0, 0);

		while (GetMessage(&msg, NULL, 0, 0) > 0)
		{
			if (msg.hwnd)
				DispatchMessage(&msg);
			else
				s_windowProc(This->m_self, msg.message, msg.wParam, msg.lParam);
		}
		
		This->destroyWindow();
	}

	// Finally, mark ourselves as stopped.
	This->beginCS();
	This->m_state = TS_STOPPED;
	This->endCS();

	_endthreadex( msg.wParam );

	return msg.wParam;
}

LRESULT CALLBACK
Thread::s_windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	Thread * This = (Thread *)GetWindowLong(hWnd, GWL_USERDATA);

//	switch (uMsg)
//	{
//		WM_CREATE:
//			This = (Thread *)(((CREATESTRUCT *)lParam)->lpCreateParams);
//			break;
//
//		default:
//			This = (Thread *)GetWindowLong(hWnd, GWL_USERDATA);
//			break;
//	}

	if (This)
		return This->windowProc(hWnd, uMsg, wParam, lParam);

	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
